In addition to common properties and methods, Visual Basic 6 forms and controls support common events. In this section, we'll describe these events in some detail.
A Click event occurs when the user left-clicks on a control, whereas the DblClick event occurs—you guessed it—when he or she double-clicks on the control using the left mouse button. But don't be fooled by this apparent simplicity because the Click event can occur under different circumstances as well. For example, whenever a CheckBox or an OptionButton control's Value property changes through code, Visual Basic fires a Click event, exactly as if the user had clicked on it. This behavior is useful because it lets you deal with the two different cases in a uniform way. ListBox and ComboBox controls also fire Click events whenever their ListIndex properties change.
Click and DblClick events don't pass arguments to the program, and therefore you can't count on these events to tell you where the mouse cursor is. To get this information, you must trap the MouseDown event instead, about which I'll say more later in this chapter. Also notice that when you double-click on a control, it receives both the Click and the DblClick events. This makes it difficult to distinguish single clicks from double-clicks because when Visual Basic calls your Click event procedure you don't know whether it will later call the DblClick procedure. At any rate, you should avoid assigning different functions to click and double-click actions on the same control because it tends to confuse the user.
TIP
While you shouldn't assign separate effects to click and double-click actions on the same control, here's a simple method to work around the problem of finding out what the user actually did:
' A module-level variable Dim isClick As Boolean Private Sub Form_Click() Dim t As Single isClick = True ' Wait for the second click for half a second. t = Timer Do DoEvents ' If the DblClick procedure canceled this event, ' bail out. If Not isClick Then Exit Sub ' The next test accounts for clicks just before midnight. Loop Until Timer > t + .5 Or Timer < t ' Do your single-click processing here. ... End Sub Private Sub Form_DblClick() ' Cancel any pending click. isClick = False ' Do your double-click processing here. ... End Sub
The Change event is the simplest event offered by Visual Basic: Whenever the contents of a control change, Visual Basic fires a Change event. Unfortunately, this simple scheme hasn't been consistently followed in the Visual Basic architecture. As I explained in the previous section, when you click on CheckBox and OptionButton controls, they fire a Click event (rather than a Change event). Fortunately, this inconsistency isn't a serious one.
TextBox and ComboBox controls raise a Change event when the user types something in the editable area of the control. (But be careful, the ComboBox control raises a Click event when the user selects an item from the list portion rather than types in a box.) Both scroll bar controls raise the Change event when the user clicks on either arrows or moves the scroll boxes. The Change event is also supported by the PictureBox, DriveListBox, and DirListBox controls.
The Change event also fires when the contents of the control are changed through code. This behavior often leads to some inefficiencies in the program. For instance, many programmers initialize the Text properties of all TextBox controls in the form's Load event, thus firing many Change events that tend to slow down the loading process.
These events are conceptually very simple: GotFocus fires when a control receives the input focus, and LostFocus fires when the input focus leaves it and passes to another control. At first glance, these events seem ideal for implementing a sort of validation mechanism—that is, a piece of code that checks the contents of a field and notifies the user if the input value isn't correct as soon as he or she moves the focus to another control. In practice, the sequence of these events is subject to several factors, including the presence of MsgBox and DoEvents statements. Fortunately, Visual Basic 6 has introduced the new Validate event, which elegantly solves the problem of field validation. (See the "The CausesValidation Property and the Validate Event" section in Chapter 3 for more details.)
Finally, note that forms support both GotFocus and LostFocus events, but these events are raised only when the form doesn't contain any control that can receive the input focus, either because all of the controls are invisible or the TabStop property for each of them is set to False.
These events fire whenever the end user presses a key while a control has the input focus. The exact sequence is as follows: KeyDown (the users presses the key), KeyPress (Visual Basic translates the key into an ANSI numeric code), and KeyUp (the user releases the key). Only keys that correspond to control keys (Ctrl+x, BackSpace, Enter, and Escape) and printable characters activate the KeyPress event. For all other keys—including arrow keys, function keys, Alt+x key combinations, and so on—this event doesn't fire and only the KeyDown and KeyUp events are raised.
The KeyPress event is the simplest of the three. It's passed the ANSI code of the key that has been pressed by the user, so you often need to convert it to a string using the Chr$() function:
Private Text1_KeyPress(KeyAscii As Integer) MsgBox "User pressed " & Chr$(KeyAscii) End Sub |
If you modify the KeyAscii parameter, your changes affect how the program interprets the key. You can also "eat" a key by setting this parameter to 0, as shown in the code below.
Private Sub Text1_KeyPress(KeyAscii As Integer) ' Convert all keys to uppercase, and reject blanks. KeyAscii = Asc(UCase$(Chr$(KeyAscii) If KeyAscii = Asc(" ") Then KeyAscii = 0 End Sub |
The KeyDown and KeyUp events receive two parameters, KeyCode and Shift. The former is the code of the pressed key, the latter is an Integer value that reports the state of the Ctrl, Shift, and Alt keys; because this value is bit-coded, you have to use the AND operator to extract the relevant information:
Private Sub Text1_KeyDown(KeyCode As Integer, Shift As Integer) If Shift And vbShiftMask Then ' Shift key pressed End If If Shift And vbCtrlMask Then ' Ctrl key pressed End If If Shift And vbAltMask Then ' Alt key pressed End If ' ... End Sub |
The KeyCode parameter tells which physical key has been pressed, and it's therefore different from the KeyAscii parameter received by the KeyPress event. You usually test this value using a symbolic constant, as in the following code:
Private Sub Text1_KeyDown(KeyCode As Integer, Shift As Integer) ' If user presses Ctrl+F2, replace the contents ' of the control with the current date. If KeyCode = vbKeyF2 And Shift = vbCtrlMask Then Text1.Text = Date$ End If End Sub |
In contrast to what you can do with the KeyPress event, you can't alter the program's behavior if you assign a different value to the KeyCode parameter.
You should note that KeyPress, KeyDown, and KeyUp events might pose special problems during the debugging phase. In fact, if you place a breakpoint inside a KeyDown event procedure, the target control will never receive a notification that a key has been pressed and the KeyPress and KeyUp events will never fire. Similarly, if you enter break mode when Visual Basic is executing the KeyPress event procedure, the target control will receive the key but the KeyUp event will never fire.
TIP
While you can't edit the KeyCode parameter and let the modified value affect the program, here's a trick that, in most cases, lets you discard an unwanted key in TextBox controls:
Private Sub Text1_KeyDown(KeyCode As Integer, Shift As Integer) If KeyCode = vbKeyDelete Then ' Make the control read-only; this actually ' discards the key. Text1.Locked = True End If End Sub Private Sub Text1_KeyUp(KeyCode As Integer, Shift As Integer) ' Restore normal operation. Text1.Locked = False End Sub
The KeyDown, KeyPress, and KeyUp events are received only by the control that has the input focus when the key is pressed. This circumstance, however, makes it difficult to create form-level key handlers, that is, code routines that monitor keys pressed in any control on the form. For example, suppose that you want to offer your users the ability to clear the current field by pressing the F7 key. You don't want to write the same piece of code in the KeyDown event procedure for each and every control on your form, and fortunately you don't have to. In fact, you only have to set the form's KeyPreview property to True (either at design time or at run time, in the Form_Load procedure, for example) and then write this code:
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer) If KeyCode = vbKeyF7 Then ' An error handler is necessary because we can't be sure ' that the active control actually supports the Text ' property. On Error Resume Next ActiveControl.Text = "" End If End Sub |
If the form's KeyPreview property is set to True, the Form object receives all keyboard-related events before they're sent to the control that currently has the input focus. Use the form's ActiveControl property if you need to act on the control with the input focus, as in the previous code snippet.
These events fire when the mouse is clicked, released, or moved on a control, respectively. All of them receive the same set of parameters: the state of mouse buttons, the state of Shift/Ctrl/Alt keys, and the x- and y-coordinates of the mouse cursor. The coordinates are always relative to the upper left corner of the control or the form. Following Figure 2-6 is a code sample that displays the status and position of the mouse on a Label control and creates a log in the Immediate window. You can see the results of running this code in Figure 2-6.
Figure 2-6. Monitor mouse state using the MouseDown, MouseMove, and MouseUp events. Note the negative y value when the cursor is outside the form's client area.
Private Sub Form_MouseDown(Button As Integer, _ Shift As Integer, X As Single, Y As Single) ShowMouseState Button, Shift, X, Y End Sub Private Sub Form_MouseMove(Button As Integer, _ Shift As Integer, X As Single, Y As Single) ShowMouseState Button, Shift, X, Y End Sub Private Sub Form_MouseUp(Button As Integer, _ Shift As Integer, X As Single, Y As Single) ShowMouseState Button, Shift, X, Y End Sub Private Sub ShowMouseState (Button As Integer, _ Shift As Integer, X As Single, Y As Single) Dim descr As String descr = Space$(20) If Button And vbLeftButton Then Mid$(descr, 1, 1) = "L" If Button And vbRightButton Then Mid$(descr, 3, 1) = "R" If Button And vbMiddleButton Then Mid$(descr, 2, 1) = "M" If Shift And vbShiftMask Then Mid$(descr, 5, 5) = "Shift" If Shift And vbCtrlMask Then Mid$(descr, 11, 4) = "Ctrl" If Shift And vbAltMask Then Mid$(descr, 16, 3) = "Alt" descr = "(" & X & ", " & Y & ") " & descr Label1.Caption = descr Debug.Print descr End Sub |
While writing code for mouse events, you should be aware of a few implementation details as well as some pitfalls in using these events. Keep in mind the following points:
It's interesting to see how MouseDown, MouseUp, and MouseMove events relate to Click and DblClick events: